//
// Comment out the #define statement below to run the 
// slow implementation.
//
#define ALLOW_UNSAFE_CODE

using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace TouchRC.Logic
{
    public static class Rotator
    {
        /// <summary>
        /// Rotates an image by right angles.
        /// </summary>
        /// <param name="rotationAngle">
        /// The <code>rotationAngle</code> must 
        /// be one of 0, 90, 180 or 270.
        /// </param>
        /// <param name="originalBitmap">The bitmap to be rotated</param>
        /// <returns>The rotated image</returns>
        public static Image RotateImage(int rotationAngle, Bitmap originalBitmap)
        {
            // Check the arguments to make sure they're valid before we try anything.
            if (rotationAngle !=   0 && rotationAngle !=  90 &&
                rotationAngle != 180 && rotationAngle != 270)
            {
                throw new ArgumentException(
                    "The rotation angle must be one of 0, 90, 180 or 270", 
                    "rotationAngle");
            }

            // If the angle is 90 or 270 it means that the width will become the height
            // and vice versa.
            bool aspectRatioChanged = (rotationAngle == 90 || rotationAngle == 270);
            int newWidth = (aspectRatioChanged ? originalBitmap.Height : originalBitmap.Width);
            int newHeight = (aspectRatioChanged ? originalBitmap.Width : originalBitmap.Height);

            // Create a new "blank" image with the width and height that we just calculated
            Bitmap rotatedBitmap = new Bitmap(newWidth, newHeight);

            //
            // Depending on wether we're allow to compile and run "unsafe" code
            // we can get either a very slow or a rather fast version of the 
            // image rotation algorithm. This is all controlled using a pre-processor
            // directive called ALLOW_UNSAFE_CODE
            //

#if ALLOW_UNSAFE_CODE            
            InternalRotateImage(rotationAngle, originalBitmap, rotatedBitmap);
#else
            InternalRotateImage(rotationAngle, originalBitmap, rotatedBitmap);
#endif
            
            return rotatedBitmap;
        }

#if ALLOW_UNSAFE_CODE            
        /// <summary>
        /// Fast running implementation of the rotation algorithm. It basically works the same as the 
        /// slower implementation from a conceptual point of view, that is it is doing pixel-by-pixel
        /// copying. The thing that makes this implementation fast is the fact that it does not use
        /// the <code>Bitmap.GetPixel</code> and <code>Bitmap.SetPixel</code> methods, but instead
        /// uses pointer arithmetic and data from aquired <code>BitmapData</code>.
        /// </summary>
        /// <param name="rotationAngle">
        /// The <code>rotationAngle</code> must 
        /// be one of 0, 90, 180 or 270.
        /// </param>
        /// <param name="originalBitmap">The source image.</param>
        /// <param name="rotatedBitmap">The destination image</param>
        private static void InternalRotateImage(int rotationAngle, Bitmap originalBitmap, Bitmap rotatedBitmap)
        {
            // It should be faster to access values stored on the stack
            // compared to calling a method (in this case a property) to 
            // retrieve a value. That's why we store the width and height
            // of the bitmaps here so that when we're traversing the pixels
            // we won't have to call more methods than necessary.
 
            int newWidth = rotatedBitmap.Width;
            int newHeight = rotatedBitmap.Height;

            int originalWidth = originalBitmap.Width;
            int originalHeight = originalBitmap.Height;

            // We're going to use the new width and height minus one alot so lets 
            // pre-calculate that once to save some more time
            int newWidthMinusOne = newWidth - 1;
            int newHeightMinusOne = newHeight - 1;

            // To grab the raw bitmap data into a BitmapData object we need to
            // "lock" the data (bits that make up the image) into system memory.
            // We lock the source image as ReadOnly and the destination image
            // as WriteOnly and hope that the .NET framework can perform some
            // sort of optimization based on this.
            // Note that this piece of code relies on the PixelFormat of the 
            // images to be 32 bpp (bits per pixel). We're not interested in 
            // the order of the components (red, green, blue and alpha) as 
            // we're going to copy the entire 32 bits as they are.
            BitmapData originalData = originalBitmap.LockBits(new Rectangle(0, 0, originalWidth, originalHeight), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
            BitmapData rotatedData = rotatedBitmap.LockBits(new Rectangle(0, 0, rotatedBitmap.Width, rotatedBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);
 
            // We're not allowed to use pointers in "safe" code so this
            // section has to be marked as "unsafe". Cool!
            unsafe
            {
                // Grap int pointers to the source image data and the 
                // destination image data. We can think of this pointer
                // as a reference to the first pixel on the first row of the 
                // image. It's actually an pointer to the piece of memory 
                // holding the int pixel data and we're going to treat it as
                // an array of one dimension later on to address the pixels.
                int* originalPointer = (int*)originalData.Scan0.ToPointer();
                int* rotatedPointer = (int*)rotatedData.Scan0.ToPointer();

                // There are nested for-loops in all of these case statements
                // and one might argue that it would have been neater and more
                // tidy to have the switch statement inside the a single nested
                // set of for loops, doing it this way saves us up to three int 
                // to int comparisons per pixel. 
                //
                // All three cases statements here do the same thing, they loop 
                // over all rows and for each row loop over all pixels in that row.
                // Then they calculate a position in the source image (this is 
                // always x + y * originalWidth) and the corresponding position 
                // in the destination image.
                // Then the destination pixel is assigned the value of the source
                // pixel.
                switch (rotationAngle)
                {
                    case 90:
                        for (int y = 0; y < originalHeight; ++y)
                        {
                            int destinationX = newWidthMinusOne - y;
                            for (int x = 0; x < originalWidth; ++x)
                            {
                                int sourcePosition = (x + y * originalWidth);
                                int destinationY = x;
                                int destinationPosition = (destinationX + destinationY * newWidth);
                                rotatedPointer[destinationPosition] = originalPointer[sourcePosition];
                            }
                        }
                        break;
                    case 180:
                        for (int y = 0; y < originalHeight; ++y)
                        {
                            int destinationY = (newHeightMinusOne - y) * newWidth;
                            for (int x = 0; x < originalWidth; ++x)
                            {
                                int sourcePosition = (x + y * originalWidth);
                                int destinationX = newWidthMinusOne - x;
                                int destinationPosition = (destinationX + destinationY);
                                rotatedPointer[destinationPosition] = originalPointer[sourcePosition];
                            }
                        }
                        break;
                    case 270:
                        for (int y = 0; y < originalHeight; ++y)
                        {
                            int destinationX = y;
                            for (int x = 0; x < originalWidth; ++x)
                            {
                                int sourcePosition = (x + y * originalWidth);
                                int destinationY = newHeightMinusOne - x;
                                int destinationPosition = (destinationX + destinationY * newWidth);
                                rotatedPointer[destinationPosition] = originalPointer[sourcePosition];
                            }
                        }
                        break;
                }

                // We have to remember to unlock the bits when we're done.
                originalBitmap.UnlockBits(originalData);
                rotatedBitmap.UnlockBits(rotatedData);
            }
        }

#else

        /// <summary>
        /// Slow, but "safe",  implementation of the image rotate algorithm. 
        /// This performs pixel by pixel copying using 
        /// <code>Bitmap.GetPixel</code> and <code>Bitmap.SetPixel</code>.
        /// </summary>
        /// <param name="rotationAngle">
        /// The <code>rotationAngle</code> must 
        /// be one of 0, 90, 180 or 270.
        /// </param>
        /// <param name="originalBitmap">The source image.</param>
        /// <param name="rotatedBitmap">The destination image</param>
        private static void InternalRotateImage(int rotationAngle, Bitmap originalBitmap, Bitmap rotatedBitmap)
        {
            // Precalculate and pre-store stuff we're going to use alot
            // later on, refer to the fast implementation of this method
            // for more detailed comments.
            int rotatedWidthMinusOne = rotatedBitmap.Width - 1;
            int rotatedHeightMinusOne = rotatedBitmap.Height - 1;
            int originalWidth = originalBitmap.Width;
            int originalHeight = originalBitmap.Height;

            // This does the same as the fast version except it uses no 
            // pointer aritmetic but GetPixel and SetPixel calls instead.
            switch (rotationAngle)
            {
                case 90:
                    for (int y = 0; y < originalHeight; ++y)
                    {
                        for (int x = 0; x < originalWidth; ++x)
                            rotatedBitmap.SetPixel(rotatedWidthMinusOne - y, x, originalBitmap.GetPixel(x, y));
                    }
                    break;
                case 180:
                    for (int y = 0; y < originalHeight; ++y)
                    {
                        for (int x = 0; x < originalWidth; ++x)
                            rotatedBitmap.SetPixel(rotatedWidthMinusOne - x, rotatedHeightMinusOne - y, originalBitmap.GetPixel(-x, y));
                    }
                    break;
                case 270:
                    for (int y = 0; y < originalHeight; ++y)
                    {
                        for (int x = 0; x < originalWidth; ++x)
                            rotatedBitmap.SetPixel(y, rotatedHeightMinusOne - x, originalBitmap.GetPixel(x, y));
                    }
                    break;
            }
        }
#endif
    }
}
